﻿using System;
using Xant.Querier.Core;

namespace Xant.Querier.Compilers
{

    public abstract class QueryVisitor
    {
        private readonly Query query;

        protected QueryVisitor(Query query)
        {
            this.query= query;
        }

        protected Query Query
        {
            get { return this.query; }
        }

        public void Visit()
        {
            if (this.query.RootExpression== null)
            {
                return;
                //throw new InvalidOperationException("Can't visit the query specification as there is no items defined under it.");
            }

            this.ProcessItem(this.query.RootExpression);
        }

        protected abstract void VisitCriteria(Criteria criteria);

        protected abstract void VisitCriteriaPair(CriteriaPair pair);

        protected abstract void VisitCriteriaNegation(CriteriaNegation negation);

        private void ProcessItem(Xpression item)
        {
            if (item is Criteria)
            {
                this.VisitCriteria(item as Criteria);
            }
            else if (item is CriteriaPair)
            {
                this.ProcessCriteriaPair(item as CriteriaPair);
            }
            else if (item is CriteriaNegation)
            {
                this.ProcessCriteriaNegation(item as CriteriaNegation);
            }
            else
            {
                throw new InvalidOperationException(
                    "Can't process the item under query specification as its type is neither Criteria, CriteriaPair, nor CriteriaNegation.");
            }
        }

        private void ProcessCriteriaNegation(CriteriaNegation negation)
        {
            this.VisitCriteriaNegation(negation);
            this.ProcessItem(negation.Body);
        }

        private void ProcessCriteriaPair(CriteriaPair pair)
        {
            this.VisitCriteriaPair(pair);
            this.ProcessItem(pair.Left);
            this.ProcessItem(pair.Right);
        }
    }
}
